package codeflyer.server;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.Socket;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

import codeFlyer.web.api.HttpApplicationContext;
import codeFlyer.web.api.HttpRequest;
import codeFlyer.web.api.HttpSession;
import codeflyer.server.utils.GetInternetIp;

/**
 * @author CodeFlyer
 * @date Apr 26, 2020
 * @version 1.0
 */

public class RequestReadByte implements HttpRequest{
	
	private Map<String,String> rheadMap = new HashMap<>(); //用来存储所有的头信息
	private Map<String,String> getParameter = new HashMap<>(); //用来存储get请求参数
	private Map<String,String> postParameter = new HashMap<>(); //用来存储post请求参数
	private Map<String, Object> request = new HashMap<>();  //reuqest的存储属性
	private byte[] uploadFile = null;  //存储上传的字节码,不是特别好,应该返回一个字节流才是最好的
	private String filePath;    //请求的路径
	private String contentType;  //请求的类型
	private Boolean isClose = false;    //请求接收完成之后,可能会得到浏览器发送的结束标志
	BufferedInputStream bis = null;
	byte[] total = null;
	private ApplicationContext applicationContext = null;
	private Session session = null;
	private String iPAndPort = null;
	
	
	@Override
	public String getIPAndPort() {
		// TODO Auto-generated method stub
		return iPAndPort;
	}

	public HttpApplicationContext getApplicationContext() {
		return applicationContext;
	}

	public void setApplicationContext(ApplicationContext applicationContext) {
		this.applicationContext = applicationContext;
	}

	public HttpSession getSession() {
		return session;
	}

	public void setSession(Session session) {
		this.session = session;
	}

	public Map<String, String> getRheadMap() {
		return rheadMap;
	}

	private void setRheadMap(Map<String, String> rheadMap) {
		this.rheadMap = rheadMap;
	}

	public Map<String, String> getGetParameter() {
		return getParameter;
	}

	private void setGetParameter(Map<String, String> getParameter) {
		this.getParameter = getParameter;
	}

	public Map<String, String> getPostParameter() {
		return postParameter;
	}

	private void setPostParameter(Map<String, String> postParameter) {
		this.postParameter = postParameter;
	}

	public String getFilePath() {
		return filePath;
	}

	private void setFilePath(String filePath) {
		this.filePath = filePath;
	}

	public String getContentType() {
		return contentType;
	}

	private void setContentType(String contentType) {
		this.contentType = contentType;
	}

	public Boolean getIsClose() {
		return isClose;
	}

	private void setIsClose(Boolean isClose) {
		this.isClose = isClose;
	}

	public byte[] getUploadFile() {
		return uploadFile;
	}

	private void setUploadFile(byte[] uploadFile) {
		this.uploadFile = uploadFile;
	}
	
	public void setAttribute(String string,Object object) {
		request.put(string, object);
	}
	
	public Object getAttribute(String string) {
		return request.get(string);
	}
	
	@Override
	public void setApplicationContext(HttpApplicationContext arg0) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void setSession(HttpSession arg0) {
		// TODO Auto-generated method stub
		
	}
	
	/**
	 * 通过io流类接收socket.getInputStream传递过来的数据
	 * @param bis
	 * @param rMessage
	 * @param total
	 * @throws IOException
	 */
	private String AcceptHttpData(InputStream bis,String rMessage) throws IOException {
		int length = 0;
		int totalNumber = 0;
		byte[] b = null;
		byte[] tCode = new byte[1024];
		//total = null;
		//在刚开始读取的时候暂停一下
		try {
			Thread.sleep(10);            
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		while((length=bis.read(tCode))==1024) {   //表示读满了字节数组
			totalNumber = totalNumber+length;
			System.out.println("超过了1024个字节---->每一次的1024:"+totalNumber);
			if(total != null) {
				b = total;
				total = new byte[totalNumber];
				System.arraycopy(b, 0, total, 0, b.length);
				System.arraycopy(tCode, 0, total, totalNumber-length, tCode.length); 
			}else {
				total = new byte[totalNumber];
				System.arraycopy(tCode, 0, total, totalNumber-length, tCode.length);   	
			}		
			//在下一次读取的时候进行等待,等待数据到来
			try {
				Thread.sleep(5);           
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		System.out.println("读到的数据长度:"+length); //只是显示最后一次读取的长度
		if(length == -1) {                       //浏览器关闭数据流时发出-1请求
			return "";        //直接跳出该方法
		}
		if(total == null) {
			total = tCode;
			rMessage = new String(total,"UTF-8");
//			System.out.println("测试读取的数据1"+rMessage);
			return rMessage;
		}else {
			b = total;
			total = new byte[totalNumber+length];                 //数组大小完全对应所读取的字节数量
			System.arraycopy(b, 0, total, 0, b.length);
			System.arraycopy(tCode, 0, total, totalNumber-length,length);
			//通过total进行解析得到请求代码最后的字符串字节码个数
			System.out.println("最终的总长度:------->"+totalNumber);
			return rMessage = new String(total,"UTF-8");
		}
	}


	/**
	 * 接受数据并打印到控制台
	 * @param socket
	 */
	public void receive(Socket socket) {
		iPAndPort = "/"+socket.getInetAddress().toString()+":"+socket.getLocalPort();
		String hmethod = null;  //请求的方法
		String[] rArray = null; //请求经过\r\n分割的请求内容
		String path = null; //请求的路径
		String fileContent = null;
		String fileBody = null;
		String[] totalArray = null;  //根据\r\n\r\n分出的两大部分
//		byte[] total = null;
		String rMessage = "";
		try {
			//直接读字节流而不是读字符串
			InputStream is = socket.getInputStream();
			bis = new BufferedInputStream(is);       //包装成缓冲输入流,默认缓存大小8M
			
			rMessage = AcceptHttpData(bis,rMessage);
			if(rMessage == "") {                       //浏览器关闭数据流时发出-1请求
				setIsClose(true);
				return;        //直接跳出该方法
			}
			System.out.println("请求的数据:"+rMessage);       //为什么打印了一个1
			//解析请求报文拿取到请求路径和所有请求头(传文件字节数组失真问题)
			
			rArray = rMessage.split("\\r\\n");   //这里的划分规则对multipart不管用
			System.out.println(rArray.length);
			System.out.println(rArray[0]);              //打印解析的 请求行部分
			//第一步解析请求行
			String rLine = rArray[0].trim();
			String[] rLArray = rLine.split(" ");
			hmethod = rLArray[0];
			path = rLArray[1];			
			setFilePath(path);                   //放入解析出来的路径 uri
			String protocol = rLArray[2];
			System.out.println("请求方法:"+hmethod+" "+"请求路径"+path+" "+"请求协议"+protocol);
			if(hmethod.equals("POST")) {
				//先通过两个\r\n来进行分割得到数据和请求部分
				totalArray = rMessage.split("\\r\\n\\r\\n");     
				rArray = totalArray[0].split("\\r\\n");         //头部分
				fileContent = totalArray[1];                    //得到数据信息部分
				System.out.println("==============测试获取的请求数据信息部分:"+fileContent);
			}
					
			//判断是什么类型的请求
			switch (hmethod) {  			
			case "GET":
				//第二部获取所有请求头,以键值对的方式存放到一个Map里
				for(int i = 1;i<=(rArray.length-3);i++) {    
					String[] rhArray = rArray[i].trim().split(": ");
					rheadMap.put(rhArray[0], rhArray[1]);
				}
				System.out.println(rheadMap);                       //打印一下get请求的头部部分
				setContentType(rheadMap.get("Accept"));				
				if(path.contains("?")) {   //如果包含参数就从请求中解析并获取参数
					//如果是GET请求获取路径里的参数
					String[] pArray = path.split("\\?");
					String[] t1Array = pArray[1].split("&");
					for(int i = 0;i<t1Array.length;i++) {
						String[] t2Array = t1Array[i].split("=");
						getParameter.put(t2Array[0], t2Array[1]);
					}
					System.out.println(getParameter);
				}else {
					
				}
				break;
			case "POST":    //post的请求可以传文件也可以传键值对
				//Post请求解析头部获取键值对放入到map中
				for(int i = 1;i<=(rArray.length-1);i++) {    //如果是post的请求如何获取请求数据呢?
					String[] rhArray = rArray[i].trim().split(": ");
					System.out.println("键:"+rhArray[0]+" 值:"+rhArray[1]);  
					rheadMap.put(rhArray[0], rhArray[1]);					
				}
				System.out.println(rheadMap);            //打印获取到的头部键值对
				
				if(rheadMap.get("Content-Type").equals("application/x-www-form-urlencoded")) {
					//常用的情况
					setContentType(rheadMap.get("Accept"));
					System.out.println("请求体部分------------->"+fileContent);    //打印请求体部分
					//解析数据
					String[] postContents = fileContent.split("&");
					for (String string : postContents) {
						String[] postContent = string.split("=");
						postParameter.put(postContent[0], postContent[1]);
					}
					System.out.println("放入map中的post参数"+postParameter);     //测试获取参数post参数是否成功					
				}else if(rheadMap.get("Content-Type").contains("application/x-www-form-urlencoded")) { //假如不是通过utf-8进行编码的数据
					//头部可能还会携带编码格式,需要进一步解析
					setContentType(rheadMap.get("Accept"));
					String rContent = fileContent; //这里的rContent需要进行解码
					//获取到头中给你的编码信息
					String ctString = rheadMap.get("Content-Type");
					String[] ctStrings = ctString.split(";");
					String pEncodes = ctStrings[1].trim();
					String[] encodes = pEncodes.split("=");
					String encode = encodes[1];
					System.out.println("********post编码格式"+encode);              //编码格式
					byte[] postByteContent = rContent.getBytes("utf-8");           //编码
					String postContent = new String(postByteContent,encode);
					//解析数据
					String[] postContents = postContent.split("&");
					for (String string : postContents) {
						String[] encodeContent = string.split("=");
						postParameter.put(encodeContent[0], encodeContent[1]);
					}
					System.out.println("放入map中的post参数"+postParameter);     //测试获取参数post参数是否成功
				}else if(rheadMap.get("Content-Type").contains("multipart/form-data;")) {
					//获得文件的信息部分的字节数
					int fc = fileContent.trim().getBytes("UTF-8").length;
					fc = fc + ("\r\n\r\n".getBytes("UTF-8").length);
					//解析获取文件的那一段字节数组
					fileBody = totalArray[2];   //得到除文件信息部分的数据部分和最后的结束分割符
					String[] fbs = fileBody.trim().split("\\r\\n");        //拆分最后一段结束分隔符
					String overMark = "\r\n"+fbs[1];
					System.out.println("====我是结束分割符====="+overMark);
					byte[] oM = overMark.getBytes("utf-8");
					System.out.println("分隔符的长度"+oM.length);               //输出一下这段分隔符的字节长度
					//从rheadMap重拿取到数据的字节个数
					String byteNumber = rheadMap.get("Content-Length");
					int byteNum = Integer.valueOf(byteNumber);     //把字符串转为数字
					System.out.println("我是文件长度=========="+byteNum);				
					//得到http请求的字节码total  7928  42
					int totalNum = total.length;  //得到所有请求的字节个数
					byte[] fileByte = new byte[byteNum-fc-oM.length];              //完整的文件内容7928多了
					System.arraycopy(total, totalNum-byteNum+fc, fileByte, 0, fileByte.length);
					System.out.println("*****************************读取实际的部分*******************************");
					uploadFile = fileByte;    //把上传的文件数组交给属性
					//System.out.println(new String(total, totalNum-byteNum+"\r\n\r\n".getBytes().length, byteNum));
					//这里没有写文件是因为,写文件的操作交给Servlet来处理,也就是开发人员来处理
				}
				break;
			default:
				break;
			}			
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	
	}

}


